/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright (c) Huawei Technologies Co., Ltd. 2019-2023. All rights reserved.
 *
 * Created on: 2019-10-10
 *
 */
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>

#include "securec.h"

#include "udk_usrnl_in.h"
#include "udk_spinlock.h"
#include "udk_log.h"
#include "udk_usrnl.h"

#define USRNL_VERSION 0x1

/* *
 * nlmsg_data - head of message payload
 * @nlh: netlink message header
 */
static inline void *usrnl_nlmsg_data(const struct usrnl_msghdr *nlh)
{
    return (unsigned char *)nlh + USRNL_HDRLEN;
}

/* *
 * nla_data - head of payload
 * @nla: netlink attribute
 */
static inline void *usrnl_nla_data(const struct usrnl_nlattr *nla)
{
    return (char *)nla + USRNL_NLA_HDRLEN;
}

static int usrnl_getsockopt_int(int fd, int level, int option, const char *optname, int *valuep)
{
    socklen_t len;
    int value;
    int error;

    len = sizeof(value);
    if (getsockopt(fd, level, option, &value, &len) != 0) {
        error = errno;
        UDK_LOG(WARNING, USRNL, "getsockopt(%s): %d\n", optname, error);
    } else if (len != sizeof(value)) {
        error = EINVAL;
        UDK_LOG(WARNING, USRNL, "getsockopt(%s): value is (%u) bytes (expected: %lu)\n",
            optname, (unsigned int)len, sizeof(value));
    } else {
        error = 0;
    }

    *valuep = (error != 0) ? 0 : value;
    return error;
}

static int usrnl_get_socket_rcvbuf(int sock)
{
    int rcvbuf;
    int error;

    error = usrnl_getsockopt_int(sock, SOL_SOCKET, SO_RCVBUF, "SO_RCVBUF", &rcvbuf);
    return (error != 0) ? -error : rcvbuf;
}

static int usrnl_sock_create(int protocol, struct usrnl_sock **sockp)
{
    struct usrnl_sock *sock = NULL;
    struct usrnl_sockaddr local, remote;
    socklen_t local_size;
    int retval = 0;
    int rcvbuf;

    *sockp = NULL;
    sock = (struct usrnl_sock *)calloc(1, sizeof(*sock));
    if (sock == NULL) {
        UDK_LOG(ERR, USRNL, "Socket alloc failed\n");
        return -ENOMEM;
    }

    sock->fd = socket(AF_NETLINK, SOCK_RAW, protocol);
    if (sock->fd < 0) {
        UDK_LOG(ERR, USRNL, "fcntl: %d\n", errno);
        retval = errno;
        goto error;
    }

    sock->protocol = protocol;
    sock->next_seq = 1;

    rcvbuf = VPMD_NLM_RCV_BUF_SIZE;
    retval = setsockopt(sock->fd, SOL_SOCKET, SO_RCVBUFFORCE, &rcvbuf, (uint32_t)sizeof(rcvbuf));
    if (retval != 0) {
        if (errno != EPERM) {
            UDK_LOG(WARNING, USRNL, "Setting %d-byte socket receive buffer Failed, err: %d\n", rcvbuf, errno);
        }
    }

    retval = usrnl_get_socket_rcvbuf(sock->fd);
    if (retval < 0) {
        retval = -retval;
        goto error;
    }
    sock->rcvbuf = retval;
    retval = 0;

    /* Connect to kernel (pid 0) as remote address. */
    (void)memset_s(&remote, sizeof(remote), 0, sizeof(remote));
    remote.nl_family = AF_NETLINK;
    remote.nl_pid = 0;
    if (connect(sock->fd, (struct sockaddr *)&remote, (uint32_t)sizeof(remote)) < 0) {
        UDK_LOG(ERR, USRNL, "connect(0) Failed, err: %d", errno);
        retval = -ENOEXEC;
        goto error;
    }

    /* Obtain pid assigned by kernel. */
    local_size = sizeof(local);
    if (getsockname(sock->fd, (struct sockaddr *)&local, &local_size) < 0) {
        UDK_LOG(ERR, USRNL, "getsockname Failed, err: %d\n", errno);
        retval = ENOEXEC;
        goto error;
    }

    if (local_size < sizeof(local) || local.nl_family != AF_NETLINK) {
        UDK_LOG(ERR, USRNL, "getsockname returned bad Netlink name\n");
        retval = ENOEXEC;
        goto error;
    }
    sock->pid = local.nl_pid;

    *sockp = sock;
    return 0;

error:
    if (sock->fd >= 0) {
        close(sock->fd);
        sock->fd = -1;
    }
    free(sock);
    return retval;
}

static void usrnl_sock_destroy(struct usrnl_sock *sock)
{
    if (sock != NULL) {
        close(sock->fd);
        sock->fd = -1;
        free(sock);
    }
}

static int usrnl_sock_pool_alloc(struct udk_usrnl *usrnl, int protocol, struct usrnl_sock **sockp)
{
    struct usrnl_sock *sock = NULL;
    struct usrnl_sock_pool *pool = &usrnl->sock_pool;
    int err = 0;

    udk_spinlock_lock(&usrnl->sock_pool_lock);
    usrnl->total_active_sock++;
    if (pool->n > 0) {
        sock = pool->socks[--pool->n];
    }
    udk_spinlock_unlock(&usrnl->sock_pool_lock);

    if (sock != NULL) {
        *sockp = sock;
    } else {
        err = usrnl_sock_create(protocol, sockp);
    }

    if (err != 0) {
        udk_spinlock_lock(&usrnl->sock_pool_lock);
        usrnl->total_active_sock--;
        udk_spinlock_unlock(&usrnl->sock_pool_lock);
    }
 
    return err;
}

static void usrnl_sock_pool_release(struct udk_usrnl *usrnl, struct usrnl_sock *sock)
{
    struct usrnl_sock_pool *pool = NULL;

    if (sock != NULL) {
        pool = &usrnl->sock_pool;

        udk_spinlock_lock(&usrnl->sock_pool_lock);
        usrnl->total_active_sock--;
        if (pool->n < USRNL_SOCK_NUM) {
            pool->socks[pool->n++] = sock;
            sock = NULL;
        }
        udk_spinlock_unlock(&usrnl->sock_pool_lock);

        usrnl_sock_destroy(sock);
    }
}

static inline char *usrnl_cmdbuf_prepend(struct usrnl_cmd_buf *cmd_buf, uint16_t len)
{
    if (cmd_buf->hdr_size > USRNL_CMD_HDR_LEN_MAX)
        return NULL;

    cmd_buf->hdr_size += len;
    cmd_buf->hdr_ptr -= len;

    return cmd_buf->hdr_ptr;
}

static inline void usrnl_cmdbuf_prerecv(struct usrnl_cmd_buf *cmd_buf)
{
    cmd_buf->hdr_size = USRNL_CMD_HDR_LEN_MAX;
    cmd_buf->hdr_ptr = (char *)cmd_buf->buf;
    cmd_buf->data_ptr = cmd_buf->hdr_ptr;
}

static inline void usrnl_cmdbuf_receieved(struct usrnl_cmd_buf *cmd_buf, uint16_t total_len, uint16_t hdr_len)
{
    cmd_buf->hdr_size = hdr_len;
    cmd_buf->data_size = total_len - hdr_len;
    cmd_buf->data_ptr = cmd_buf->hdr_ptr + hdr_len;
}

static struct usrnl_cmd_buf *usrnl_cmdbuf_alloc(uint16_t size)
{
    int buf_size;
    struct usrnl_cmd_buf *cmd_buf = NULL;

    cmd_buf = calloc(1, sizeof(*cmd_buf));
    if (cmd_buf == NULL) {
        UDK_LOG(ERR, USRNL, "Allocate cmd_buf failed \n");
        return NULL;
    }

    buf_size = USRNL_CMD_BUF_LEN_MAX + USRNL_CMD_HDR_LEN_MAX;
    cmd_buf->buf = calloc(1, buf_size);
    if (!cmd_buf->buf) {
        UDK_LOG(ERR, USRNL, "Allocate cmd_buf buffer memory failed \n");
        goto alloc_cmd_buf_err;
    }

    cmd_buf->hdr_size = 0;
    cmd_buf->data_size = (uint16_t)USRNL_NLA_ALIGN(size);
    cmd_buf->data_ptr = ((char *)cmd_buf->buf) + USRNL_CMD_HDR_LEN_MAX;
    cmd_buf->hdr_ptr = cmd_buf->data_ptr;

    return cmd_buf;

alloc_cmd_buf_err:
    free(cmd_buf);

    return NULL;
}

static void usrnl_cmdbuf_free(struct usrnl_cmd_buf *cmd_buf)
{
    if (cmd_buf != NULL) {
        free(cmd_buf->buf);
        free(cmd_buf);
    }
}

struct usrnl_cmd_buf *usrnl_cmd_pool_alloc(void *usrnl_hdl, uint16_t size)
{
    struct usrnl_cmd_buf *cmd = NULL;
    struct usrnl_cmd_pool *pool = NULL;
    struct udk_usrnl *usrnl = (struct udk_usrnl *)usrnl_hdl;

    if ((size > USRNL_CMD_BUF_LEN_MAX) || (usrnl == NULL)) {
        UDK_LOG(ERR, USRNL, "invalid input size:%d, usrnl %d\n", size, !!usrnl);
        return NULL;
    }

    if (size == 0) {
        size = USRNL_CMD_BUF_LEN_MAX;
    }

    pool = &usrnl->cmd_pool;

    udk_spinlock_lock(&usrnl->cmd_pool_lock);
    if (pool->n > 0) {
        cmd = pool->cmd_bufs[--pool->n];
    }
    udk_spinlock_unlock(&usrnl->cmd_pool_lock);

    if (cmd != NULL) {
        cmd->hdr_size = 0;
        cmd->data_size = (uint16_t)USRNL_NLA_ALIGN(size);
        cmd->data_ptr = ((char *)cmd->buf) + USRNL_CMD_HDR_LEN_MAX;
        cmd->hdr_ptr = cmd->data_ptr;

        return cmd;
    } else {
        return usrnl_cmdbuf_alloc(size);
    }
}

void usrnl_cmd_pool_free(void *usrnl_hdl, struct usrnl_cmd_buf *buf)
{
    int buf_size;
    struct usrnl_cmd_pool *pool = NULL;
    struct udk_usrnl *usrnl = (struct udk_usrnl *)usrnl_hdl;

    if (usrnl == NULL || buf == NULL) {
        UDK_LOG(ERR, USRNL, "invalid args\n");
        return;
    }

    pool = &usrnl->cmd_pool;

    udk_spinlock_lock(&usrnl->cmd_pool_lock);
    if (pool->n < USRNL_CMD_NUM) {
        /* clear buffer as zero */
        buf_size = USRNL_CMD_BUF_LEN_MAX + USRNL_CMD_HDR_LEN_MAX;
        (void)memset_s(buf->buf, buf_size, 0, buf_size);

        /* record buffer in cache */
        pool->cmd_bufs[pool->n++] = buf;
        buf = NULL;
    }
    udk_spinlock_unlock(&usrnl->cmd_pool_lock);

    if (buf != NULL) {
        usrnl_cmdbuf_free(buf);
    }

    return;
}

static int usrnl_sock_send(struct usrnl_sock *sock, const struct usrnl_cmd_buf *msg)
{
    int rc = 0;
    struct msghdr socket_msg;
    struct iovec iov;
    struct usrnl_sockaddr dest_addr;

    /* turn cmd_buf into iovec */
    iov.iov_base = USRNL_CMD_BUF_ADDR(msg);
    iov.iov_len = (size_t)USRNL_CMD_BUF_LEN(msg);

    (void)memset_s(&dest_addr, sizeof(dest_addr), 0, sizeof(dest_addr));
    dest_addr.nl_family = AF_NETLINK;
    dest_addr.nl_pid = 0;    /* kernel */
    dest_addr.nl_groups = 0; /* unicast */

    (void)memset_s(&socket_msg, sizeof(socket_msg), 0, sizeof(socket_msg));
    socket_msg.msg_name = (void *)&dest_addr;
    socket_msg.msg_namelen = sizeof(dest_addr);
    socket_msg.msg_iov = &iov;
    socket_msg.msg_iovlen = 1;

    /* in kernel, netlink_sendmsg always add MSG_DONTWAIT flag */
    rc = (int)sendmsg(sock->fd, &socket_msg, 0);
    if (rc <= 0) {
        UDK_LOG(ERR, USRNL, "Sendmsg rc: %d, errno: %d, ctrl_fd: %d\n", rc, errno, sock->fd);
        return -ENOEXEC;
    }

    return 0;
}

static int usrnl_sock_recv(struct usrnl_sock *sock, struct usrnl_cmd_buf *buf, uint16_t exp_hdr_len, bool wait_flag)
{
    int rc;
    struct iovec iov;
    struct msghdr socket_msg;
    struct usrnl_sockaddr src_addr;
    struct usrnl_msghdr *nlmsg_hdr = NULL;
    struct usrnl_msgerr *errmsg = NULL;

    /* set cmd buffer for receive */
    usrnl_cmdbuf_prerecv(buf);

    /* turn mbuf into iovec */
    iov.iov_base = USRNL_CMD_BUF_ADDR(buf);
    iov.iov_len = (size_t)USRNL_CMD_BUF_LEN(buf);

    (void)memset_s(&src_addr, sizeof(src_addr), 0, sizeof(src_addr));
    (void)memset_s(&socket_msg, sizeof(socket_msg), 0, sizeof(socket_msg));
    socket_msg.msg_name = (void *)&src_addr;
    socket_msg.msg_namelen = sizeof(src_addr);
    socket_msg.msg_iov = &iov;
    socket_msg.msg_iovlen = 1;

    rc = (int)recvmsg(sock->fd, &socket_msg, wait_flag ? 0 : MSG_DONTWAIT);
    if (rc > 0) {
        nlmsg_hdr = (struct usrnl_msghdr *)buf->hdr_ptr;
        if (nlmsg_hdr->nlmsg_type == NLMSG_ERROR) {
            errmsg = (struct usrnl_msgerr *)usrnl_nlmsg_data(nlmsg_hdr);
            rc = errmsg->error;
            return rc;
        }

        if (rc >= exp_hdr_len) {
            usrnl_cmdbuf_receieved(buf, (uint16_t)rc, exp_hdr_len);
            rc = 0;
        } else {
            rc = -EIO;
        }
    } else {
        UDK_LOG(ERR, USRNL, "Recv rc: %d, errno: %d, ctrl_fd: %d\n", rc, errno, sock->fd);
    }

    return rc;
}

static void usrnl_put_nlmsghdr(struct usrnl_cmd_buf *msg, uint16_t type, uint16_t flags)
{
    char *new_data = usrnl_cmdbuf_prepend(msg, USRNL_HDRLEN);
    struct usrnl_msghdr *nlhdr = (struct usrnl_msghdr *)new_data;

    nlhdr->nlmsg_len = (uint32_t)USRNL_CMD_BUF_LEN(msg);
    nlhdr->nlmsg_type = type;
    nlhdr->nlmsg_flags = flags;
    nlhdr->nlmsg_seq = 0;
    nlhdr->nlmsg_pid = 0;
}

static void usrnl_put_genlmsghdr(struct usrnl_cmd_buf *msg, uint8_t cmd, uint8_t version)
{
    char *new_data = usrnl_cmdbuf_prepend(msg, (uint16_t)USRNL_GENL_HDRLEN);
    struct genlmsghdr *genlhdr = (struct genlmsghdr *)new_data;

    genlhdr->cmd = cmd;
    genlhdr->version = version;
    genlhdr->reserved = 0;
}

/* make sure msg->pkt_len aligns in 4-byte */
static void usrnl_put_nlattrhdr(struct usrnl_cmd_buf *msg, uint16_t type)
{
    char *new_data = usrnl_cmdbuf_prepend(msg, USRNL_NLA_HDRLEN);
    struct usrnl_nlattr *nlamsg = (struct usrnl_nlattr *)new_data;

    /* nlattr init */
    nlamsg->nla_len = (uint16_t)USRNL_CMD_BUF_LEN(msg);
    nlamsg->nla_type = type;
}

static int usrnl_do_lookup_genl_family(struct udk_usrnl *usrnl, struct usrnl_cmd_buf *req, uint32_t *number)
{
    int error = 0;
    uint16_t exp_hdr_len;
    struct usrnl_sock *sock = NULL;
    struct usrnl_nlattr *nla = NULL;
    struct usrnl_cmd_buf *rsp = NULL;

    error = usrnl_sock_create(NETLINK_GENERIC, &sock);
    if (error != 0) {
        UDK_LOG(ERR, USRNL, "Create nl_sock  failed, err: %d\n", error);
    }

    rsp = usrnl_cmd_pool_alloc(usrnl, USRNL_CMD_BUF_LEN_MAX);
    if (rsp == NULL) {
        error = -ENOMEM;
        UDK_LOG(ERR, USRNL, "Alloc cmd buffer failed\n");
        goto alloc_rsp_cmd_fail;
    }

    /* build request */
    usrnl_put_nlattrhdr(req, UDK_CTRL_ATTR_FAMILY_NAME);
    usrnl_put_genlmsghdr(req, UDK_CTRL_CMD_GETFAMILY, 1);
    usrnl_put_nlmsghdr(req, GENL_ID_CTRL, NLM_F_REQUEST);

    /* send request via netlink socket */
    error = usrnl_sock_send(sock, req);
    if (error != 0) {
        UDK_LOG(ERR, USRNL, "Sock[%d] send Failed\n", sock->fd);
        goto sock_sndrcv_err;
    }

    /* receive response */
    exp_hdr_len = (uint16_t)(USRNL_HDRLEN + USRNL_GENL_HDRLEN);
    error = usrnl_sock_recv(sock, rsp, exp_hdr_len, true);
    if (error != 0) {
        UDK_LOG(ERR, USRNL, "sock[%d] recv Failed, err: %d\n", sock->fd, error);
        goto sock_sndrcv_err;
    }

    /* get nla header */
    nla = USRNL_CMD_DATA_ADDR(rsp, struct usrnl_nlattr *);

    /* skip FAMILY_NAME and get FAMILY_ID */
    nla = (struct usrnl_nlattr *)((char *)nla + USRNL_NLA_ALIGN(nla->nla_len));
    if (nla->nla_type == UDK_CTRL_ATTR_FAMILY_ID) {
        *number = *(uint16_t *)usrnl_nla_data(nla);
    } else {
        *number = 0;
    }

sock_sndrcv_err:
    usrnl_cmd_pool_free(usrnl, rsp);
alloc_rsp_cmd_fail:
    usrnl_sock_destroy(sock);

    return error;
}

static int usrnl_lookup_genl_family(struct udk_usrnl *usrnl, const char *name, uint32_t *number)
{
    int error;
    int16_t pld_len;
    struct usrnl_cmd_buf *req = NULL;

    pld_len = (int16_t)strlen(name) + 1;

    /* alloc usrnl cmd buf */
    req = usrnl_cmd_pool_alloc(usrnl, pld_len);
    if (req == NULL) {
        UDK_LOG(ERR, USRNL, "Alloc buffer for get family id failed\n");
        return -ENOMEM;
    }

    /* copy data playload */
    (void)memcpy_s(USRNL_CMD_DATA_ADDR(req, void *), USRNL_CMD_BUF_LEN_MAX, (void *)name, pld_len);

    /* find kernel genl family by name */
    error = usrnl_do_lookup_genl_family(usrnl, req, number);
    if (error != 0 || *number == 0) {
        UDK_LOG(ERR, USRNL, "Get family id failed, err:%d, number:%d\n", error, *number);
    }

    usrnl_cmd_pool_free(usrnl, req);

    return error;
}

static int32_t usrnl_init_internal(struct udk_usrnl *usrnl, const char *nl_family_name)
{
    int error = 0;

    UDK_LOG(INFO, USRNL, "netlink init us family:%s\n", nl_family_name);

    udk_spinlock_init(&usrnl->sock_pool_lock);
    (void)memset_s(&usrnl->sock_pool, sizeof(usrnl->sock_pool), 0, sizeof(usrnl->sock_pool));

    udk_spinlock_init(&usrnl->cmd_pool_lock);
    (void)memset_s(&usrnl->cmd_pool, sizeof(usrnl->cmd_pool), 0, sizeof(usrnl->cmd_pool));

    error = usrnl_lookup_genl_family(usrnl, nl_family_name, &usrnl->usrnl_family_id);
    if (error != 0) {
        UDK_LOG(ERR, USRNL, "Generic Netlink family (%s) does not exist. "
            "The kernel module is probably not loaded.\n",
            nl_family_name);
    }

    return error;
}

static void usrnl_deinit_internal(struct udk_usrnl *usrnl)
{
    int i = 0;
    struct usrnl_sock_pool *nl_pool = NULL;
    struct usrnl_cmd_pool *cmd_pool = NULL;

    UDK_LOG(INFO, USRNL, "netlink deinit\n");

    nl_pool = &usrnl->sock_pool;
    cmd_pool = &usrnl->cmd_pool;

    /* close netlink socket */
    udk_spinlock_lock(&usrnl->sock_pool_lock);
    for (i = 0; i < nl_pool->n; i++) {
        usrnl_sock_destroy(nl_pool->socks[i]);
        nl_pool->socks[i] = NULL;
    }
    nl_pool->n = 0;
    udk_spinlock_unlock(&usrnl->sock_pool_lock);

    /* release cmd buffer */
    udk_spinlock_lock(&usrnl->cmd_pool_lock);
    for (i = 0; i < cmd_pool->n; i++) {
        usrnl_cmdbuf_free(cmd_pool->cmd_bufs[i]);
        cmd_pool->cmd_bufs[i] = NULL;
    }
    nl_pool->n = 0;
    udk_spinlock_unlock(&usrnl->cmd_pool_lock);
}

int32_t usrnl_send_cmd_recv_rsp(void *usrnl_hdl, uint16_t cmd, struct usrnl_cmd_buf *cmd_in,
    struct usrnl_cmd_buf *rsp_out)
{
    int rc = 0;
    uint16_t exp_hdr_len;
    uint32_t family_id;
    struct usrnl_sock *sock = NULL;
    struct udk_usrnl *usrnl = (struct udk_usrnl*)usrnl_hdl;

    if (usrnl == NULL || cmd_in == NULL || rsp_out == NULL) {
        UDK_LOG(ERR, USRNL, "invalid args\n");
        return -EINVAL;
    }

    if (usrnl->usrnl_status == USRNL_STATUS_BLOCK) {
        UDK_LOG(ERR, USRNL, "now is kexecing, usr netlink is blocking\n");
        return -EBUSY;
    }

    family_id = usrnl->usrnl_family_id;

    /* get a socket */
    rc = usrnl_sock_pool_alloc(usrnl, NETLINK_GENERIC, &sock);
    if (rc != 0) {
        UDK_LOG(ERR, USRNL, "nl_pool_alloc failed, rc=%d\n", rc);
        return rc;
    }

    /* put netlink header */
    usrnl_put_nlattrhdr(cmd_in, USRNL_GENL_ATTR);
    usrnl_put_genlmsghdr(cmd_in, (uint8_t)cmd, (uint8_t)USRNL_VERSION);
    usrnl_put_nlmsghdr(cmd_in, (uint16_t)family_id, NLM_F_REQUEST);

    /* send to kernel */
    rc = usrnl_sock_send(sock, cmd_in);
    if (rc != 0) {
        UDK_LOG(ERR, USRNL, "Sock send Failed\n");
        goto out_rls;
    }

    /* get response from kernel */
    exp_hdr_len = (uint16_t)(USRNL_HDRLEN + USRNL_GENL_HDRLEN + USRNL_NLA_HDRLEN);
    rc = usrnl_sock_recv(sock, rsp_out, exp_hdr_len, false);
    if (rc != 0) {
        UDK_LOG(ERR, USRNL, "Sock recv Failed |error(%d)\n", rc);
        goto out_rls;
    }

out_rls:
    /* release the socket */
    usrnl_sock_pool_release(usrnl, sock);

    return rc;
}

static usrnl_wait_return usrnl_wait_sockets_ready(void *usrnl_hdl)
{
    struct udk_usrnl *usrnl = (struct udk_usrnl *)usrnl_hdl;

    if (usrnl->total_active_sock > 0) {
        return USRNL_PROCESS_WAITING;
    }

    return USRNL_PROCESS_CPL;
}

static int32_t usrnl_wait_for_timeout(void *priv_data, usrnl_wait_cpl_handler handler,
                                      uint32_t try_times, uint32_t wait_once_us)
{
    usrnl_wait_return ret;

    if (!handler) {
        UDK_LOG(ERR, USRNL, "handler is null\n");
        return -EINVAL;
    }

    do {
        ret = handler(priv_data);
        if (ret == USRNL_PROCESS_CPL) {
            return 0;
        } else if (ret == USRNL_PROCESS_ERR) {
            UDK_LOG(ERR, USRNL, "wait usrnl flushing error\n");
            return -EIO;
        }

        (void)usleep(wait_once_us);
    } while (try_times--);

    return -ETIMEDOUT;
}

static void usrnl_sock_in_pool_destroy(struct udk_usrnl *usrnl)
{
    struct usrnl_sock_pool *pool = &usrnl->sock_pool;
    struct usrnl_sock *sock = NULL;

    while (pool->n) {
        sock = pool->socks[--pool->n];
        usrnl_sock_destroy(sock);
    };
}

int32_t netlink_block_and_get_flush_status(bool flag, void *usrnl_hdl)
{
    struct udk_usrnl *usrnl = (struct udk_usrnl *)usrnl_hdl;
    int32_t err;

    if (!usrnl_hdl) {
        UDK_LOG(ERR, USRNL, "invalid args\n");
        return -EINVAL;
    }

    usrnl->usrnl_status = flag ? USRNL_STATUS_BLOCK : USRNL_STATUS_NORMAL;

    if (usrnl->usrnl_status == USRNL_STATUS_BLOCK) {
        err = usrnl_wait_for_timeout(usrnl_hdl, usrnl_wait_sockets_ready, USRNL_FLUSH_TRY_TIMES, USRNL_FLUSH_WAIT_ONCE_US);
        if (err != 0) {
            UDK_LOG(ERR, USRNL, "wait usrnl socket flush timeout\n");
            return err;
        }

        usrnl_sock_in_pool_destroy(usrnl);
    }

    return 0;
}

int32_t usrnl_exec_cmdq_cmd(void *usrnl, struct usrnl_cmd_buf *cmd_in, struct usrnl_cmd_buf *cmd_out)
{
    return usrnl_send_cmd_recv_rsp((struct udk_usrnl*)usrnl, USRNL_DEV_CMDQ_CMD_EXECUTE, cmd_in, cmd_out);
}

int32_t usrnl_exec_mgmt_msg(void *usrnl, struct usrnl_cmd_buf *cmd_in, struct usrnl_cmd_buf *rsp_out)
{
    return usrnl_send_cmd_recv_rsp((struct udk_usrnl*)usrnl, USRNL_DEV_MPU_CMD_EXECUTE, cmd_in, rsp_out);
}

int32_t usrnl_exec_drv_cmd(void *usrnl, struct usrnl_cmd_buf *cmd_in, struct usrnl_cmd_buf *rsp_out)
{
    return usrnl_send_cmd_recv_rsp((struct udk_usrnl*)usrnl, USRNL_DEV_DRV_CMD_EXECUTE, cmd_in, rsp_out);
}

int32_t usrnl_exec_vf_cmd(void *usrnl, struct usrnl_cmd_buf *cmd_in, struct usrnl_cmd_buf *rsp_out)
{
    return usrnl_send_cmd_recv_rsp((struct udk_usrnl*)usrnl, USRNL_DEV_VF_CMD_EXECUTE, cmd_in, rsp_out);
}

int32_t usrnl_create(void **usrnl_hdl, const char *nl_family_name)
{
    int rc;
    struct udk_usrnl *usrnl_alloc = NULL;

    if (usrnl_hdl == NULL || nl_family_name == NULL) {
        return -EINVAL;
    }

    usrnl_alloc = calloc(1, sizeof(*usrnl_alloc));
    if (usrnl_alloc == NULL) {
        UDK_LOG(ERR, USRNL, "Alloc usrnl memory failed\n");
        return -ENOMEM;
    }

    rc = usrnl_init_internal(usrnl_alloc, nl_family_name);
    if (rc != 0) {
        usrnl_deinit_internal(usrnl_alloc);
        free(usrnl_alloc);
    } else {
        usrnl_alloc->usrnl_status = USRNL_STATUS_NORMAL;
        *usrnl_hdl = usrnl_alloc;
    }

    return rc;
}

void usrnl_destroy(void *usrnl_hdl)
{
    if (usrnl_hdl == NULL) {
        return;
    }

    usrnl_deinit_internal((struct udk_usrnl*)usrnl_hdl);
    free(usrnl_hdl);
}
